package net.sql.config;

import java.io.File;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.dbcp.BasicDataSource;
import org.dom4j.Document;
import org.dom4j.Element;

import net.dotool.Dom4jTools;
import net.dotool.NumberTools;
import net.dotool.StringTools;
import net.sql.i.DatabasePageCk;
import net.sql.i.DatabasePageImpala;
import net.sql.i.DatabasePageMySQL;
import net.sql.i.DatabasePageOracle;
import net.sql.i.DatabasePagePresto;
import net.sql.i.IDatabasePage;

/**
 * 
 * @author 李岩飞
 * @email eliyanfei@126.com 2018年3月2日 上午11:11:13
 *
 */
public class SQLFactory {
	private static SQLFactory config = new SQLFactory();
	private CacheSqlPool cachePool = null;

	public static SQLFactory getConfig() {
		return config;
	}

	public CacheSqlPool getCachePool() {
		return cachePool;
	}

	// 分页器
	private Map<String, IDatabasePage> pageMap = new HashMap<String, IDatabasePage>();
	private Map<String, BasicDataSource> dbMap = new HashMap<String, BasicDataSource>();
	private Map<String, Map<String, String>> dbParamMap = new HashMap<String, Map<String, String>>();
	private Map<String, String> dbTypeMap = new HashMap<String, String>();
	private Map<String, String> belongsMap = new HashMap<String, String>();

	private String defaultDbname = null;

	public Map<String, String> getParams(String name) {
		return dbParamMap.get(name);
	}

	/**
	 * 获取数据库名称
	 * 
	 * @param belong
	 * @return
	 */
	public String getDbName(String belong) {
		return StringTools.trimNull(belongsMap.get(belong), defaultDbname);
	}

	/**
	 * 获取数据连接
	 */
	public synchronized Connection getConnectionByBelong(String name) throws SQLException {
		return getConnectionByBelong(name, defaultDbname);
	}

	private boolean checkPrestoName(String dbName) {
		if (dbName.startsWith("presto")) {
			return true;
		}
		return false;
	}

	public Connection getPrestoConnection(String dbName) {
		Connection conn = null;
		try {
			BasicDataSource db = dbMap.get(dbName);
			Map<String, String> params = dbParamMap.get(dbName);

			Class.forName(db.getDriverClassName());
			String url = db.getUrl();
			Properties properties = new Properties();
			properties.setProperty("SSLKeyStorePath", params.get("SSLKeyStorePath"));
			properties.setProperty("SSLKeyStorePassword", params.get("SSLKeyStorePassword"));
			properties.setProperty("SSL", "true");
			properties.setProperty("user", db.getUsername());
			properties.setProperty("password", db.getPassword());

			properties.setProperty("KerberosRemoteServiceName", "HTTP");
			properties.setProperty("KerberosPrincipal", params.get("KerberosPrincipal"));
			properties.setProperty("KerberosKeytabPath", params.get("KerberosKeytabPath"));
			properties.setProperty("KerberosConfigPath", params.get("KerberosConfigPath"));
			properties.setProperty("KerberosServicePrincipalPattern", params.get("KerberosServicePrincipalPattern"));
			conn = DriverManager.getConnection(url, properties);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return conn;

	}

	/**
	 * 根据数据分类获取数据源,如果没有分类，根据名称获取数据源
	 */
	public synchronized Connection getConnectionByBelong(String name, String dbName) throws SQLException {
		try {
			dbName = StringTools.trimNull(belongsMap.get(name), dbName);
			if (checkPrestoName(dbName)) {
				return getPrestoConnection(dbName);
			}

			return dbMap.get(dbName).getConnection();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	public synchronized BasicDataSource getDataSourceByBelong(String name) throws SQLException {
		return getDataSourceByBelong(name, defaultDbname);
	}

	/**
	 * 根据数据分类获取数据源,如果没有分类，根据名称获取数据源
	 */
	public synchronized BasicDataSource getDataSourceByBelong(String name, String dbName) throws SQLException {
		try {
			dbName = StringTools.trimNull(belongsMap.get(name), dbName);
			return dbMap.get(dbName);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 根据默认的名称获取数据源
	 */
	public synchronized Connection getConnection() throws SQLException {
		try {
			if (checkPrestoName(defaultDbname)) {
				return getPrestoConnection(defaultDbname);
			}
			return dbMap.get(defaultDbname).getConnection();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 获取对应的数据库名
	 */
	public Collection<String> getDbNames() {
		return dbMap.keySet();
	}

	/**
	 * 根据指定的名称获取数据源
	 */
	public synchronized Connection getConnection(String name) throws SQLException {
		try {
			String dbName = StringTools.trimNull(name, defaultDbname);
			if (checkPrestoName(dbName)) {
				return getPrestoConnection(dbName);
			}
			return dbMap.get(dbName).getConnection();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 根据默认的名称获取数据源
	 */
	public synchronized BasicDataSource getDataSource() throws SQLException {
		try {
			return dbMap.get(defaultDbname);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 根据指定的名称获取数据源
	 */
	public synchronized BasicDataSource getDataSource(String name) throws SQLException {
		try {
			return dbMap.get(StringTools.trimNull(name, defaultDbname));
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	public void loadConfig(String path, String dbname) {
		try {
			if (!dbMap.isEmpty()) {
				return;
			}
			this.defaultDbname = dbname;
			final File file = new File(path + File.separator + "oracle.xml");
			final Document doc = Dom4jTools.readDom4jXmlDocument(new FileInputStream(file));
			final Element rootEle = doc.getRootElement();
			try {
				int cache = NumberTools.toInt(rootEle.attributeValue("cache"), 10);
				cachePool = new CacheSqlPool(cache);// 开启缓存;
				List<Element> dbs = rootEle.elements();
				for (Element db : dbs) {
					try {
						String name = db.attributeValue("name");
						String type = db.attributeValue("type");
						String belongs = db.attributeValue("belongs");
						String source = StringTools.trimNull(db.attributeValue("source"), "");
						if (StringTools.isNotEmpty(belongs)) {
							for (String b : belongs.split(",")) {
								belongsMap.put(source + b, name);
							}
						}
						dbTypeMap.put(name, type);
						List<Element> eleList = db.elements();
						if (eleList != null) {
							Map<String, String> paramMap = new HashMap<String, String>();
							for (Element ele : eleList) {
								paramMap.put(ele.attributeValue("key"), ele.getText());
							}
							BasicDataSource dataSource = dbMap.get(name);
							if (dataSource != null) {
								dataSource.close();
							} else {
								dataSource = new BasicDataSource();
							}
							dataSource.setDriverClassName(paramMap.get("driverClassName"));
							dataSource.setUrl(paramMap.get("url"));
							dataSource.setUsername(paramMap.get("username"));
							dataSource.setPassword(paramMap.get("password"));
							dataSource.setInitialSize(50);
							dataSource.setMaxWait(-1);
							dataSource.setMaxIdle(300);
							dataSource.setMaxActive(500);
							dataSource.setTestWhileIdle(true);
							dataSource.setTimeBetweenEvictionRunsMillis(300000);
							if ("mysql".equals(type))
								dataSource.setValidationQuery("SELECT 1");
							if ("oracle".equals(type))
								dataSource.setValidationQuery(
										StringTools.trimNull(paramMap.get("query"), "select 1 from dual"));
							dataSource.setTestOnBorrow(true);

							dbMap.put(name, dataSource);
							dbParamMap.put(name, paramMap);
						}
					} catch (Exception e) {
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			loadPageClass();
		}
	}

	/**
	 * 加载配置
	 */
	public void loadConfig(String path) {
		loadConfig(path, null);
	}

	private void loadPageClass() {
		try {
			IDatabasePage page = new DatabasePageMySQL();
			pageMap.put(page.getName(), page);
			page = new DatabasePageOracle();
			pageMap.put(page.getName(), page);
			page = new DatabasePageImpala();
			pageMap.put(page.getName(), page);
			page = new DatabasePageCk();
			pageMap.put(page.getName(), page);
			page = new DatabasePagePresto();
			pageMap.put(page.getName(), page);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public IDatabasePage getPage(String dbName) {
		String type = dbTypeMap.get(dbName);
		return pageMap.get(type);
	}
}
